package com.sghd.football.module.game.service;

import com.sghd.common.console.ConsoleBean;
import com.sghd.common.console.ConsoleCommand;
import com.sghd.common.socket.core.Message;
import com.sghd.common.socket.core.ResultCallback;
import com.sghd.common.socket.session.Session;
import com.sghd.common.socket.session.SessionManager;
import com.sghd.football.module.data.service.DataService;
import com.sghd.football.module.game.facade.model.JoinResult;
import com.sghd.football.module.game.service.config.AbstractTemplete;
import com.sghd.football.module.game.service.config.ConfigService;
import com.sghd.football.module.game.service.config.model.ConfigRoom;
import com.sghd.football.module.game.service.core.Desk;
import com.sghd.football.module.game.service.core.Player;
import com.sghd.football.module.game.service.core.Room;
import com.sghd.football.module.game.service.core.task.CleanTask;
import com.sghd.football.module.game.service.core.task.TimeoutTask;
import com.sghd.football.module.game.service.match.Matcher;
import com.sghd.football.module.game.service.model.Medal;
import com.sghd.football.module.game.service.model.PropType;
import com.sghd.football.module.game.service.model.Window;
import com.sghd.football.module.game.service.thread.Task;
import com.sghd.football.module.game.service.thread.TaskItem;
import com.sghd.football.module.game.service.thread.ThreadService;
import com.sghd.football.module.game.service.thread.Work;
import com.sghd.football.net.PushReceiver;
import com.sghd.football.net.SocketEnum;
import com.sghd.football.utils.ManagedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 *
 *                       _oo0oo_
 *                      o8888888o
 *                      88" . "88
 *                      (| -_- |)
 *                      0\  =  /0
 *                    ___/`---'\___
 *                  .' \\|     |// '.
 *                 / \\|||  :  |||// \
 *                / _||||| -:- |||||- \
 *               |   | \\\  -  /// |   |
 *               | \_|  ''\---/''  |_/ |
 *               \  .-\__  '-'  ___/-. /
 *             ___'. .'  /--.--\  `. .'___
 *          ."" '<  `.___\_<|>_/___.' >' "".
 *         | | :  `- \`.;`\ _ /`;.`/ - ` : | |
 *         \  \ `_.   \_ __\ /__ _/   .-` /  /
 *     =====`-.____`.___ \_____/___.-`___.-'=====
 *                       `=---='
 *
 *     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *               佛祖保佑         永无BUG
 *  Created with 王创辉.
 *  Date: 2018/5/18
 *  Time: 12:08
 *  @desc: 游戏逻辑服务
 */
@ConsoleBean
@Component
public class GameService implements Work, PushReceiver, GameServiceMBean {

    public static final Logger logger = LoggerFactory.getLogger(GameService.class);
    /*** 线程服务*/
    @Autowired
    private ThreadService threadService;
    /*** 数据访问服务，结算*/
    @Autowired
    private DataService dataService;
    /*** 会话管理器，推送消息*/
    @Autowired
    private SessionManager sessionManager;
    /*** 房间列表<房间id, 房间>*/
    private ConcurrentMap<Integer, Room> rooms = new ConcurrentHashMap<>();
    /*** 所有玩家<玩家id, 玩家>*/
    private ConcurrentMap<Long, Player> players = new ConcurrentHashMap<>();
    @Autowired
    private ConfigService configService;
    @Autowired
    private DialogService dialogService;

    @Autowired
    private AbstractTemplete configTemplete;
    @Autowired
    private Matcher matcher;
    /**超时*/
    @Value("${game.timeout:180000}")
    private int timeout;
    /**10分钟内重连*/
    @Value("${game.reconn:600000}")
    private int reconn;
    private AtomicInteger counter = new AtomicInteger(0);

    @PostConstruct
    void init() {
        // 注册MBean
        try {
            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            ObjectName name = new ObjectName("com.sghd.football:type=GameServiceMBean");
            mbs.registerMBean(this, name);
        } catch (Exception e) {
            logger.error("注册[GameServiceMBean]的JMX管理接口失败", e);
        }
    }

    /**
     * 匹配线程调用
     * @param player
     */
    public void conn(Player player) {
        long playerId = player.getPlayerId();
        if (players.containsKey(playerId)) {//重连分支
            Player prev = players.get(playerId);
            prev.setReconn(true);//重连
            prev.setLastUpdate(new Date());
        } else {
            CleanTask cleanTask = new CleanTask(player, reconn, this);
            addTimer(null, cleanTask, new Date(), TaskItem.MATCH);
            players.put(playerId, player);
        }
    }

    /**
     * 放入匹配队列
     * */
    public void match(long playerId) {
        Player player = dataService.getPlayeInfo(playerId);//获取玩家基本信息
        if (null == player) {
            return;
        }
        threadService.submit(() -> {//netty线程切换到匹配线程执行
            matcher.add(player);//玩家进入匹配队列
        });
    }

    public void enter(long playerId, int configId, int gameId, ResultCallback<JoinResult> callback) {
        threadService.submit(() -> {
            try {
                Player player = players.get(playerId);
                ConfigRoom configRoom = configService.getConfig(configId);
                player.setGameId(gameId);
                int lastRoomId = player.getRoomId();
                int roomId = configRoom.getRoomId();
                if (lastRoomId != -1 && lastRoomId != roomId) {
                    throw new ManagedException(Window.OTHER_ROOM);//在另一个房间中，需要重连
                }
                player.setRoomId(configRoom.getRoomId());
                Room room = rooms.get(roomId);
                if (room == null) {
                    room = new Room(this, roomId, configRoom, dataService, this, counter);
                    Room absent = rooms.putIfAbsent(roomId, room);
                    room = absent != null ? absent : room;
                }
                if (dataService.intoRoom(playerId, gameId, roomId)) {//保存房间进入记录
                    //获取玩家最新背包数据
                    dataService.getBagInfo(player);
                    JoinResult result = null;//
                    try {
                        result = room.enter(player);
                        result.setMyMoney(player.getMondey(PropType.GOLD));
                        //超时踢下线
                        if (!player.isTmt()) {//判断是否已经添加过超时任务，防止重连多次添加超时任务
                            //3分钟超时退出
                            TimeoutTask timeoutTask = new TimeoutTask(player, timeout, this, sessionManager);
                            addTimer(null, timeoutTask, new Date(), TaskItem.MATCH);
                            player.setTmt(true);
                        }
                        callback.call(result);
                    } catch (ManagedException e) {
                        exit(playerId, true);//进入房间失败强制退出
                        dataService.outRoom(playerId, gameId, roomId);
                        dialogService.dialog(((ManagedException) e).getWindow(), playerId);//进入房间失败提示
                    } catch (Exception e) {
                        exit(playerId, true);//进入房间失败强制退出
                        dataService.outRoom(playerId, gameId, roomId);
                        logger.error("玩家[{}]进入房间失败!", playerId, e);
                    }
                }
            } catch (ManagedException e) {
                dialogService.dialog(((ManagedException) e).getWindow(), playerId);//进入房间失败提示
            } catch (Exception e) {
                logger.error("玩家[{}]进入房间失败!", playerId, e);
            }
        });
    }

    @ConsoleCommand(name = "reload")
    public void reload() {
        configTemplete.reload();
    }

    public Map<String, Object> operator(long playerId, int betIdx) throws Exception {
        Player player = players.get(playerId);
        if (null == player) {
            throw new ManagedException(Window.PLAYER_NOT_FOUND);
        }
        int roomId = player.getRoomId();
        Room room = rooms.get(roomId);
        if (room == null) {
            throw new ManagedException(Window.UNKOUNW_ERROR);
        }
        return room.operator(player, betIdx);
    }

    @ConsoleCommand(name = "sel")
    public void sel() {
        long cou = rooms.get(0).drawWaters.get();
        System.out.println("抽水池：" + cou);
        Room room = rooms.get(0);
        Map<Integer, AtomicLong> refund = room.refundPools;
        long ref = 0l;
        for (AtomicLong atomicLong : refund.values()) {
            ref += atomicLong.get();
        }
        System.out.println("反分池：" + ref);

        Map<Medal, AtomicLong> rew = room.rewardPool;
        long rewL = 0;
        for (AtomicLong atomicLong : rew.values()) {
            rewL += atomicLong.get();
        }
        System.out.println("奖池：" + rewL);
        System.out.println("合计：" + (cou + ref + rewL));
        long playerMoney = 0l;
        for (Player player : players.values()) {
            playerMoney+=player.getMondey(PropType.GOLD);
        }
        System.out.println("玩家金币总和："+playerMoney);
    }

    /**
     * @param playerId
     * @param rely     是不是真退出
     */
    public void exit(long playerId, boolean rely) {
        Player player = players.get(playerId);
        if (null == player) {
            return;
        }
        if (player.isExit() && !rely) {
            return;
        }
        int roomId = player.getRoomId();
        Room room = rooms.get(roomId);
        if (room != null) {
            room.exit(player, rely);
        }
        if (rely) {
            players.remove(playerId);
            player = null;
        } else {
            dataService.outRoom(playerId, player.getGameId(), roomId);//退出房间
            player.setExit(true);
        }
    }

    public Room getRoom(int roomId) {
        return rooms.get(roomId);
    }

    @Override
    public void addTimer(Desk desk, Task task, Date time) {
        threadService.add(new TaskItem(desk, task, time));
    }

    public void addTimer(Desk desk, Task task, Date time, int type) {
        threadService.add(new TaskItem(desk, task, time, type));
    }

    @Override
    public void addTimer(TaskItem work) {
        threadService.add(work);
    }

    @Override
    public void submit(Desk desk, Task task) {
        threadService.submit(desk.getId(), () -> {
            task.run(desk, new Date());
        });
    }

    @Override
    public void submit(int id, Runnable runnable) {
        threadService.submit(id, runnable);
    }

    @Override
    public void submit(Runnable runnable) {
        threadService.submit(runnable);
    }

    @Override
    public void push(Message message, Object... ids) {
        sessionManager.send(message, ids);
    }

    @Override
    public void dialog(Window window, Object... ids) {
        dialogService.dialog(window, ids);
    }

    public void destory() {
        threadService.close();//匹配线程停止接收任务
        Collection<Session> onlines = sessionManager.getOnlineSessions();
        Message msg = Message.valueOf(SocketEnum.INFO, Window.SERVER_CLOSED.getWindows());
        onlines.stream().forEach(session -> {
            session.write(msg);
        });
        sessionManager.kickAll(Session.NORMAL);
        logger.error("[{}]个会话已关闭", onlines.size());
    }

    //jvm
    @Override
    public long get反分池0() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(0);
    }

    @Override
    public long get反分池1() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(1);
    }

    @Override
    public long get反分池2() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(2);
    }

    @Override
    public long get反分池3() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(3);
    }

    @Override
    public long get反分池4() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(4);
    }

    @Override
    public long get反分池5() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(5);
    }

    @Override
    public long get反分池6() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(6);
    }

    @Override
    public long get反分池7() {
        Room room = rooms.get(0);
        if (room == null) {
            return 0;
        }
        return room.getRefundPool(7);
    }

    @Override
    public int get在线人数() {
        return sessionManager.getOnlineSessions().size();
    }
}
